package cn.ticsmyc.tools.multiThread.guardedSuspension;

import java.util.concurrent.Callable;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author Ticsmyc
 * @date 2021-05-31 15:55
 */
public class ConditionVarBlocker implements Blocker{

    private final Lock lock;
    private final Condition condition;


    public ConditionVarBlocker() {
        this.lock = new ReentrantLock();
        this.condition = this.lock.newCondition();
    }

    public ConditionVarBlocker(Lock lock) {
        this.lock = lock;
        this.condition = lock.newCondition();
    }

    /**
     * 如果保护条件成立，就执行目标动作。 否则阻塞当前线程，知道保护条件成立。
     *
     * @param guardedAction 目标动作
     * @return
     * @throws Exception
     */
    @Override
    public <V> V callWithGuard(GuardedAction<V> guardedAction) throws Exception {
        V result;
        lock.lockInterruptibly();
        try{
            final Predicate guard = guardedAction.guard;
            while(!guard.evaluate()){
                condition.await();
            }
            result = guardedAction.call();
            return result;
        }finally {
            lock.unlock();
        }
    }

    /**
     * 执行stateOperation改变状态，然后决定是否唤醒一个线程
     *
     * @param stateOperation
     * @throws Exception
     */
    @Override
    public void signalAfter(Callable<Boolean> stateOperation) throws Exception {
        lock.lockInterruptibly();
        try{
            if(stateOperation.call()){
                condition.signal();
            }
        }finally {
            lock.unlock();
        }
    }

    @Override
    public void signal() throws InterruptedException {
        lock.lockInterruptibly();
        try{
            condition.signal();
        }finally {
            lock.unlock();
        }
    }

    /**
     * 执行stateOperation改变状态，然后决定是否唤醒所有线程
     *
     * @param stateOperation
     * @throws Exception
     */
    @Override
    public void broadcastAfter(Callable<Boolean> stateOperation) throws Exception {
        lock.lockInterruptibly();
        try{
            if(stateOperation.call()){
                condition.signalAll();
            }
        }finally {
            lock.unlock();
        }
    }
}
